Valdage React Contexti tellimusi, et tõhusalt, peeneteraliselt värskendada globaalseid rakendusi, vältides tarbetuid ümberrenderdamisi ja parandades jõudlust.
React Contexti tellimus: peeneteralised värskenduste juhtimine globaalsetes rakendustes
Kaasaegse veebiarenduse dünaamilises maastikus on tõhus olekuhaldus ülioluline. Kuna rakendused muutuvad keerukamaks, eriti need, millel on globaalne kasutajaskond, muutub komponentide ümberrenderdamine ainult siis, kui see on vajalik, kriitiliseks jõudlusprobleemiks. Reacti Context API pakub võimsat viisi oleku jagamiseks kogu teie komponentide puus ilma propide puurimiseta. Levinuks komistuskiviks on aga selliste komponentide tarbetute ümberrenderdamiste käivitamine, mis tarbivad konteksti, isegi kui ainult väike osa jagatud olekust on muutunud. See postitus sukeldub React Contexti tellimuste peeneteralise värskenduste juhtimise kunsti, andes teile võimaluse luua jõudluslikumaid ja skaleeritavamaid globaalseid rakendusi.
React Contexti ja selle ümberrenderdamise käitumise mõistmine
React Context pakub mehhanismi andmete edastamiseks läbi komponentide puu ilma, et oleks vaja proppe käsitsi igal tasemel alla panna. See koosneb kolmest peamisest osast:
- Konteksti loomine: Konteksti objekti loomiseks kasutades
React.createContext(). - Provider: Komponent, mis pakub konteksti väärtust oma järeltulijatele.
- Consumer: Komponent, mis tellib konteksti muudatusi. Ajalooliselt tehti seda
Context.Consumerkomponendiga, kuid nĂĽĂĽd sagedamini saavutatakse seeuseContexthooki abil.
Põhiprobleem tekib sellest, kuidas Reacti Context API värskendusi käsitleb. Kui Context Provideri pakutud väärtus muutub, renderdatakse vaikimisi ümber kõik seda konteksti tarbivad komponendid (otseselt või kaudselt). See käitumine võib põhjustada märkimisväärseid jõudlusummikuid, eriti suurtes rakendustes või kui konteksti väärtus on keeruline ja sageli värskendatud. Kujutage ette globaalset teemajuhendit, kus muutub ainult esmane värv. Ilma korraliku optimeerimiseta renderdatakse ümber kõik teemakonteksti kuulavad komponendid, isegi need, kes kasutavad ainult fondi perekonda.
Probleem: laiade ĂĽmberrenderdamiste korral funktsiooniga `useContext`
Illustreerime vaikimisi käitumist tavalise stsenaariumiga. Oletame, et meil on kasutajaprofiili kontekst, mis sisaldab erinevaid kasutajateabe osi: nimi, e-post, eelistused ja teavituste arv. Paljud komponendid võivad vajada juurdepääsu nendele andmetele.
// UserContext.js
import React, { createContext, useState, useContext } from 'react';
const UserContext = createContext();
export const UserProvider = ({ children }) => {
const [user, setUser] = useState({
name: 'Global Citizen',
email: 'citizen@example.com',
preferences: { theme: 'dark', language: 'en' },
notificationCount: 0,
});
const updateNotificationCount = (count) => {
setUser(prevUser => ({ ...prevUser, notificationCount: count }));
};
return (
{children}
);
};
export const useUser = () => useContext(UserContext);
NĂĽĂĽd kaaluge kahte seda konteksti tarbivat komponenti:
// UserNameDisplay.js
import React from 'react';
import { useUser } from './UserContext';
const UserNameDisplay = () => {
const { user } = useUser();
console.log('UserNameDisplay rendered');
return User Name: {user.name};
};
export default UserNameDisplay;
// UserNotificationCount.js
import React from 'react';
import { useUser } from './UserContext';
const UserNotificationCount = () => {
const { user, updateNotificationCount } = useUser();
console.log('UserNotificationCount rendered');
return (
Notifications: {user.notificationCount}
);
};
export default UserNotificationCount;
Teie peamises App komponendis:
// App.js
import React from 'react';
import { UserProvider } from './UserContext';
import UserNameDisplay from './UserNameDisplay';
import UserNotificationCount from './UserNotificationCount';
function App() {
return (
Global User Dashboard
{/* Muud komponendid, mis võivad UserContexti tarbida või mitte */}
);
}
export default App;
Kui klõpsate nuppu "Add Notification" komponendis UserNotificationCount, renderdatakse ümber nii UserNotificationCount kui ka UserNameDisplay, kuigi UserNameDisplay hoolib ainult kasutaja nimest ega huvi tunne teavituste arvu vastu. Seda seetõttu, et kogu user objekt konteksti väärtuses on värskendatud, käivitades ümberrenderdamise kõigile UserContext tarbijatele.
Peeneteraliste värskenduste strateegiad
Peeneteraliste värskenduste saavutamise võti on tagada, et komponendid telliksid ainult vajalikke oleku osi. Siin on mitmeid tõhusaid strateegiaid:
1. Konteksti jagamine
Kõige otsem ja sageli kõige tõhusam lähenemisviis on jagada oma kontekst väiksemateks, fokuseeritumateks kontekstideks. Kui teie rakenduse erinevad osad vajavad globaalse oleku erinevaid lõike, looge nende jaoks eraldi kontekstid.
Refaktoreerime eelmise näite:
// UserProfileContext.js
import React, { createContext, useContext } from 'react';
const UserProfileContext = createContext();
export const UserProfileProvider = ({ children, profileData }) => {
return (
{children}
);
};
export const useUserProfile = () => useContext(UserProfileContext);
// UserNotificationsContext.js
import React, { createContext, useContext, useState } from 'react';
const UserNotificationsContext = createContext();
export const UserNotificationsProvider = ({ children }) => {
const [notificationCount, setNotificationCount] = useState(0);
const addNotification = () => {
setNotificationCount(prev => prev + 1);
};
return (
{children}
);
};
export const useUserNotifications = () => useContext(UserNotificationsContext);
Ja kuidas te neid kasutaksite:
// App.js
import React from 'react';
import { UserProfileProvider } from './UserProfileContext';
import { UserNotificationsProvider } from './UserNotificationsContext';
import UserNameDisplay from './UserNameDisplay'; // Kasutab endiselt useUserProfile
import UserNotificationCount from './UserNotificationCount'; // Kasutab nĂĽĂĽd useUserNotifications
function App() {
const initialProfileData = {
name: 'Global Citizen',
email: 'citizen@example.com',
preferences: { theme: 'dark', language: 'en' },
};
return (
Global User Dashboard
);
}
export default App;
// UserNameDisplay.js (värskendatud kasutama UserProfileContext)
import React from 'react';
import { useUserProfile } from './UserProfileContext';
const UserNameDisplay = () => {
const userProfile = useUserProfile();
console.log('UserNameDisplay rendered');
return User Name: {userProfile.name};
};
export default UserNameDisplay;
// UserNotificationCount.js (värskendatud kasutama UserNotificationsContext)
import React from 'react';
import { useUserNotifications } from './UserNotificationsContext';
const UserNotificationCount = () => {
const { notificationCount, addNotification } = useUserNotifications();
console.log('UserNotificationCount rendered');
return (
Notifications: {notificationCount}
);
};
export default UserNotificationCount;
Selle jaotusega, kui teavituste arv muutub, renderdatakse ümber ainult UserNotificationCount. UserNameDisplay, mis tellib UserProfileContext, ei renderda ümber, kuna selle konteksti väärtus pole muutunud. See on märkimisväärne parandus jõudluse osas.
Globaalsed kaalutlused: Kontekstide jagamisel globaalse rakenduse jaoks kaaluge loogilist murede eraldamist. Näiteks globaalsel ostukorvil võib olla eraldi kontekstid üksuste, koguhinna ja kassaprotsessi oleku jaoks. See peegeldab seda, kuidas erinevad globaalse ettevõtte osakonnad oma andmeid sõltumatult haldavad.
2. Memoisatsioon funktsioonidega `React.memo` ja `useCallback`/`useMemo`
Isegi kui teil on üks kontekst, saate tarbivaid komponente memoisatsiooniga optimeerida. React.memo on kõrgem komponendi funktsioon, mis memoiserib teie komponendi. See teostab komponentide eelneva ja uue propsi madalat võrdlust. Kui need on samad, jätab React komponendi ümberrenderdamise vahele.
Kuid useContext ei tööta propsi mõistes; see käivitab ümberrenderdamisi konteksti väärtuse muudatuste põhjal. Kui konteksti väärtus muutub, renderdatakse tarbiv komponent efektiivselt ümber. React.memo tõhusaks kasutamiseks kontekstiga peate tagama, et komponent saab kontekstist konkreetseid andmeosi propsi kujul või et konteksti väärtus ise on stabiilne.
Täpsem muster hõlmab konteksti provideri sees valimisfunktsioonide loomist. Need valijad võimaldavad tarbivate komponentide tellida oleku konkreetseid lõike ja providerit saab optimeerida, et see teavitaks tellijaid ainult siis, kui nende konkreetne lõige muutub. Seda rakendatakse sageli kohandatud hookide abil, mis kasutavad useContext ja `useMemo`.
Vaatame uuesti ühe konteksti näidet, kuid eesmärgiga saavutada rohkem granulaarseid värskendusi ilma konteksti jagamata:
// UserContextImproved.js
import React, { createContext, useContext, useState, useMemo, useCallback } from 'react';
const UserContext = createContext();
export const UserProvider = ({ children }) => {
const [user, setUser] = useState({
name: 'Global Citizen',
email: 'citizen@example.com',
preferences: { theme: 'dark', language: 'en' },
notificationCount: 0,
});
// Memoiserige oleku konkreetseid osi, kui neid edastatakse propsi kujul
// või kui loote kohandatud hooke, mis tarbivad konkreetseid osi.
const updateNotificationCount = useCallback((count) => {
setUser(prevUser => {
// Loob uue kasutajobjekti ainult siis, kui notificationCount muutub
if (prevUser.notificationCount === count) return prevUser;
return {
...prevUser,
notificationCount: count,
};
});
}, []);
// Pakkuge stabiilseid või ainult vajadusel värskenduvaid konkreetseid valijaid/väärtusi
const contextValue = useMemo(() => ({
user: {
name: user.name,
email: user.email,
preferences: user.preferences
// Võimalusel välistage notificationCount sellest memoiseritud väärtusest
},
notificationCount: user.notificationCount,
updateNotificationCount
}), [user.name, user.email, user.preferences, user.notificationCount, updateNotificationCount]);
return (
{children}
);
};
// Kohandatud hookid konteksti konkreetsete lõikude jaoks
export const useUserName = () => {
const { user } = useContext(UserContext);
// `React.memo` tarbival komponendil töötab, kui `user.name` on stabiilne
return user.name;
};
export const useUserNotifications = () => {
const { notificationCount, updateNotificationCount } = useContext(UserContext);
// `React.memo` tarbival komponendil töötab, kui `notificationCount` ja `updateNotificationCount` on stabiilsed
return { notificationCount, updateNotificationCount };
};
Refaktoreerige nĂĽĂĽd tarbivad komponendid, et kasutada neid granulaarseid hooke:
// UserNameDisplay.js
import React from 'react';
import { useUserName } from './UserContextImproved';
const UserNameDisplay = React.memo(() => {
const userName = useUserName();
console.log('UserNameDisplay rendered');
return User Name: {userName};
});
export default UserNameDisplay;
// UserNotificationCount.js
import React from 'react';
import { useUserNotifications } from './UserContextImproved';
const UserNotificationCount = React.memo(() => {
const { notificationCount, updateNotificationCount } = useUserNotifications();
console.log('UserNotificationCount rendered');
return (
Notifications: {notificationCount}
);
});
export default UserNotificationCount;
Selles täiustatud versioonis:
- `useCallback` kasutatakse funktsioonide jaoks nagu
updateNotificationCount, et tagada nende stabiilne identiteet ümberrenderdamiste korral, vältides ümberrenderdamisi lapsekomponentides, mis neid propsi kujul saavad. - `useMemo` kasutatakse provideri sees, et luua memoiseritud konteksti väärtus. Lisades sellesse memoiseritud objekti ainult vajalikud oleku osad (või tuletatud väärtused), saame potentsiaalselt vähendada tarbijate uue konteksti väärtuse viite saamise kordi. Oluliselt loome kohandatud hookid (
useUserName,useUserNotifications), mis ekstraheerivad konkreetsed konteksti osad. - `React.memo` rakendatakse tarbivatele komponentidele. Kuna need komponendid tarbivad nĂĽĂĽd ainult konkreetset oleku osa (nt
userNamevõinotificationCount), ja need väärtused on memoiseritud või värskendatakse ainult siis, kui nende konkreetne andmeosa muutub, saabReact.memotõhusalt vältida ümberrenderdamisi, kui kontekstis muutub seostamata olek.
Kui klõpsate nuppu, muutub user.notificationCount. Kuid providerile edastatud `contextValue` objekt võib uuesti luua. Oluline on see, et useUserName hook saab `user.name`, mis pole muutunud. Kui UserNameDisplay komponent on pakitud React.memo ja selle propsi (sel juhul useUserName tagastatav väärtus) pole muutunud, ei renderda see ümber. Samamoodi renderdab UserNotificationCount ümber, kuna selle konkreetne olekute osa (notificationCount) muutus.
Globaalsed kaalutlused: See tehnika on eriti väärtuslik globaalsete konfiguratsioonide jaoks, nagu kasutajaliidese teemad või rahvusvahelise (i18n) sätted. Kui kasutaja muudab oma eelistatud keelt, peaksid ümber renderdama ainult komponendid, mis aktiivselt kuvavad lokaliseeritud teksti, mitte iga komponent, mis võib kunagi locale andmetele juurde pääseda.
3. Kohandatud konteksti valijad (täpsem)
Äärmiselt keerukate olekustruktuuride või veelgi keerukama juhtimise vajaduse korral saate rakendada kohandatud konteksti valijaid. See muster hõlmab kõrgema komponendi või kohandatud hooki loomist, mis võtab argumendina valimisfunktsiooni. Hook tellib seejärel konteksti, kuid renderdab tarbiva komponendi ümber ainult siis, kui valimisfunktsiooni tagastatud väärtus muutub.
See on sarnane sellele, mida raamatukogud nagu Zustand või Redux saavutavad oma valijatega. Saate seda käitumist jäljendada:
// UserContextSelectors.js
import React, { createContext, useContext, useState, useMemo, useCallback, useRef, useEffect } from 'react';
const UserContext = createContext();
export const UserProvider = ({ children }) => {
const [user, setUser] = useState({
name: 'Global Citizen',
email: 'citizen@example.com',
preferences: { theme: 'dark', language: 'en' },
notificationCount: 0,
});
const updateNotificationCount = useCallback((count) => {
setUser(prevUser => {
if (prevUser.notificationCount === count) return prevUser;
return {
...prevUser,
notificationCount: count,
};
});
}, []);
// Kogu user objekt on siin lihtsuse huvides väärtus,
// kuid kohandatud hook käsitleb valimist.
const contextValue = useMemo(() => ({ user, updateNotificationCount }), [user, updateNotificationCount]);
return (
{children}
);
};
// Kohandatud hook valimisega
export const useUserContext = (selector) => {
const context = useContext(UserContext);
if (!context) {
throw new Error('useUserContext peab olema UserProvideri sees');
}
const { user, updateNotificationCount } = context;
// Memoiserige valitud väärtus, et vältida tarbetuid ümberrenderdamisi
const selectedValue = useMemo(() => selector(user), [user, selector]);
// Kasutage ref-i eelneva valitud väärtuse jälgimiseks
const previousSelectedValue = useRef();
useEffect(() => {
previousSelectedValue.current = selectedValue;
}, [selectedValue]);
// Renderdage ümber ainult siis, kui valitud väärtus on muutunud.
// `React.memo` tarbival komponendil koos sellega
// tagab tõhusad värskendused.
const isSelectedValueDifferent = selectedValue !== previousSelectedValue.current;
return {
selectedValue,
updateNotificationCount,
// See on lihtsustatud mehhanism. Tugev lahendus hõlmaks
// keerukamat tellimuse haldurit provideris.
// Näitamiseks tugineme tarbiva komponendi memoisatsioonile.
};
};
Tarbivad komponendid näeksid välja sellised:
// UserNameDisplay.js
import React from 'react';
import { useUserContext } from './UserContextSelectors';
const UserNameDisplay = React.memo(() => {
// Valimisfunktsioon kasutaja nime jaoks
const userNameSelector = (user) => user.name;
const { selectedValue: userName } = useUserContext(userNameSelector);
console.log('UserNameDisplay rendered');
return User Name: {userName};
});
export default UserNameDisplay;
// UserNotificationCount.js
import React from 'react';
import { useUserContext } from './UserContextSelectors';
const UserNotificationCount = React.memo(() => {
// Valimisfunktsioon teavituste arvu ja värskendusfunktsiooni jaoks
const notificationSelector = (user) => ({ count: user.notificationCount });
const { selectedValue, updateNotificationCount } = useUserContext(notificationSelector);
console.log('UserNotificationCount rendered');
return (
Notifications: {selectedValue.count}
);
});
export default UserNotificationCount;
Selles mustris:
useUserContexthook võtabselectorfunktsiooni.- See kasutab
useMemo, et arvutada konteksti põhjal valitud väärtus. See valitud väärtus on memoiseritud. useEffectja `useRef` kombinatsioon on lihtsustatud viis tagada, et komponent renderdatakse ümber ainult siis, kuiselectedValuetegelikult muutub. Tõeliselt tugev rakendus hõlmaks provideri sees keerukamat tellimuste haldamise süsteemi, kus tarbijad registreerivad oma valijad ja provider teavitab neid valikuliselt.React.memo-ga pakitud tarbivad komponendid renderdatakse ümber ainult siis, kui nende konkreetse valimisfunktsiooni tagastatud väärtus muutub.
Globaalsed kaalutlused: See lähenemisviis pakub maksimaalset paindlikkust. Globaalse e-kaubanduse platvormi jaoks võiksite omada ühte konteksti kõigi ostukorvi kohta käivate andmete jaoks, kuid kasutada valijaid ainult kuvatava ostukorvi üksuste arvu, vahesumma või saatmiskulu sõltumatuks värskendamiseks.
Millist strateegiat kasutada
- Konteksti jagamine: See on üldiselt eelistatav meetod enamiku stsenaariumide jaoks. See toob kaasa puhtama koodi, parema murede eraldamise ja sellele on lihtsam mõelda. Kasutage seda siis, kui teie rakenduse erinevad osad sõltuvad selgelt globaalse andmete erinevatest komplektidest.
- Memoisatsioon koos `React.memo`, `useCallback`, `useMemo` (koos kohandatud hookidega): See on hea vaheline strateegia. See aitab, kui konteksti jagamine tundub ülepingutusena, või kui üks kontekst loogiliselt hoiab tihedalt seotud andmeid. See nõuab rohkem käsitsi tööd, kuid pakub granulaarset kontrolli ühe konteksti sees.
- Kohandatud konteksti valijad: Jätke see väga keerukatele rakendustele, kus ülaltoodud meetodid muutuvad kohmakateks, või kui soovite jäljendada spetsiaalsete olekuhaldusraamatukogude keerukaid tellimismudeleid. See pakub kõige granulaarsemat kontrolli, kuid sellega kaasneb suurem keerukus.
Globaalse konteksti haldamise parimad tavad
Globaalsete rakenduste loomisel React Contextiga kaaluge neid parimaid tavasid:
- Hoidke konteksti väärtused lihtsad: Vältige suuri, monoliitseid konteksti objekte. Jaotage need loogiliselt.
- Eelistage kohandatud hooke: Konteksti tarbimise abstraheerimine kohandatud hookideks (nt
useUserProfile,useTheme) muudab teie komponendid puhtamaks ja soodustab taaskasutatavust. - Kasutage `React.memo` targalt: Ärge pakendage iga komponenti `React.memo`-ga. Profiilige oma rakendust ja rakendage seda ainult seal, kus ümberrenderdamised on jõudlusprobleem.
- Funktsioonide stabiilsus: Kasutage alati `useCallback` funktsioonide jaoks, mis edastatakse konteksti või propsi kaudu, et vältida tahtmatuid ümberrenderdamisi.
- Tuletatud andmete memoisatsioon: Kasutage `useMemo`-i mis tahes arvutatud väärtuste jaoks, mis on tuletatud kontekstist ja mida kasutavad mitu komponenti.
- Kaaluge kolmandate osapoolte raamatukogusid: Väga keerukate globaalsete olekuhalduse vajaduste jaoks pakuvad raamatukogud nagu Zustand, Jotai või Recoil sisseehitatud lahendusi peeneteraliste tellimuste ja valijate jaoks, sageli vähem paberimajandusega.
- Dokumenteerige oma kontekst: Dokumenteerige selgelt, mida iga kontekst pakub ja kuidas tarbijad peaksid sellega suhtlema. See on oluline suurte, hajutatud meeskondade jaoks, kes töötavad globaalsete projektidega.
Järeldus
Peeneteralise värskenduste juhtimise valdamine React Contextis on oluline jõudluslike, skaleeritavate ja hooldatavate globaalsete rakenduste loomiseks. Jagades strateegiliselt kontekste, kasutades memoisatsioonitehnikaid ja mõistes, millal kohandatud valimismustreid rakendada, saate oluliselt vähendada tarbetuid ümberrenderdamisi ja tagada, et teie rakendus jääb reageerivaks, sõltumata selle suurusest või oleku keerukusest.
Rakenduste loomisel, mis teenindavad kasutajaid erinevates piirkondades, ajavööndites ja võrguolukordades, muutuvad need optimeerimised mitte ainult parimateks tavadeks, vaid vajadusteks. Võtke need strateegiad kasutusele, et pakkuda oma globaalsele publikule paremat kasutajakogemust.